﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;

namespace MoroMoro.Samples.ToDoList.Base.Application
{
    /// <summary>
    /// 簡易的なエラーハンドラを生成します。
    /// </summary>
    /// <remarks>
    /// 全てのエラーハンドラメソッドで同じ処理を行うエラーハンドラを用意する場合、
    /// <see cref="ErrorsHandlerCreator&lt;THandler&gt;"/> クラスを使用することで、エラーハンドラを動的に生成することができます。
    /// これにより、エラーハンドラインターフェイスの実装クラスの定義を省略することができます。
    /// エラーハンドラメソッドの処理は、<see cref="ErrorsHandlerCreator&lt;THandler&gt;"/> クラスの利用側で <see cref="ErrorsHandlerCreator&lt;THandler&gt;.Create(Action)"/> の引数として渡します。
    /// </remarks>
    /// <typeparam name="THandler">エラーハンドラの型。</typeparam>
    public static class ErrorsHandlerCreator<THandler>
            where THandler : class
    {
        /// <summary>
        /// 簡易的なエラーハンドラを生成します。
        /// </summary>
        /// <param name="handleMethod">エラーハンドラとして実行されるメソッド。</param>
        /// <returns>エラーハンドラ。</returns>
        /// <exception cref="System.ArgumentNullException">handleMethod が null です。</exception>
        public static THandler Create(Action handleMethod)
        {
            if (handleMethod == null)
            {
                throw new ArgumentNullException("handleMethod");
            }
            var proxy = new ErrorHandlerProxy(handleMethod);
            return (THandler)proxy.GetTransparentProxy();
        }


        /// <summary>
        /// エラーハンドラインターフェイスのプロキシです。
        /// </summary>
        private sealed class ErrorHandlerProxy : RealProxy
        {
            /// <summary>
            /// <see cref="ErrorHandlerProxy"/> クラスの新しいインスタンスを初期化します。
            /// </summary>
            /// <param name="handlerMethod">エラーハンドラメソッドが呼び出された時に実行するメソッド。</param>
            public ErrorHandlerProxy(Action handlerMethod)
                : base(typeof(THandler))
            {
                _handlerMethod = handlerMethod;
            }

            /// <summary>
            /// エラーハンドラメソッドが呼び出された時に実行するメソッド。
            /// </summary>
            private readonly Action _handlerMethod;


            /// <summary>
            /// メソッド呼び出しをフックします。
            /// </summary>
            /// <param name="msg">メソッド呼び出しメッセージ。</param>
            /// <returns>メソッド呼び出しの応答メッセージ。</returns>
            public override IMessage Invoke(IMessage msg)
            {
                var methodCallMessage = (IMethodCallMessage)msg;
                _handlerMethod();
                return new ReturnMessage(null, null, 0, methodCallMessage.LogicalCallContext, methodCallMessage);
            }
        }
    }
}
